package com.android.basiclib.ext

import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.RippleDrawable
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.*
import android.util.TypedValue
import android.view.View
import android.view.WindowManager
import androidx.core.content.FileProvider
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.viewModelScope
import androidx.recyclerview.widget.RecyclerView
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.android.basiclib.base.BaseApplication
import com.android.basiclib.base.vm.BaseViewModel
import com.android.basiclib.utils.CommUtils
import com.android.basiclib.utils.NetWorkUtil
import com.android.basiclib.utils.interceptor.LoginInterceptorTask
import com.android.basiclib.engine.toast.toast
import com.android.basiclib.utils.log.MyLogUtils
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.Serializable

/**
 *  通用扩展
 */

/**
 * 全局的Context
 */
fun Any.commContext(): Context {
    return CommUtils.getContext()
}

/** dp和px转换 **/
fun dp2px(dpValue: Float): Int {
    return (dpValue * CommUtils.getContext().resources.displayMetrics.density + 0.5f).toInt()
}

fun px2dp(pxValue: Float): Int {
    return (pxValue / CommUtils.getContext().resources.displayMetrics.density + 0.5f).toInt()
}

fun sp2px(spValue: Float): Int {
    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue, CommUtils.getContext().resources.displayMetrics).toInt()
}

fun px2sp(pxValue: Float): Int {
    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, pxValue, CommUtils.getContext().resources.displayMetrics).toInt()
}


/** 动态创建Drawable **/
fun Context.createDrawable(
    color: Int = Color.TRANSPARENT, radius: Float = 0f,
    strokeColor: Int = Color.TRANSPARENT, strokeWidth: Int = 0,
    enableRipple: Boolean = true,
    rippleColor: Int = Color.parseColor("#88999999")
): Drawable {
    val content = GradientDrawable().apply {
        setColor(color)
        cornerRadius = radius
        setStroke(strokeWidth, strokeColor)
    }
    if (Build.VERSION.SDK_INT >= 21 && enableRipple) {
        return RippleDrawable(ColorStateList.valueOf(rippleColor), content, null)
    }
    return content
}

fun Fragment.createDrawable(
    color: Int = Color.TRANSPARENT, radius: Float = 0f,
    strokeColor: Int = Color.TRANSPARENT, strokeWidth: Int = 0,
    enableRipple: Boolean = true,
    rippleColor: Int = Color.parseColor("#88999999")
): Drawable {
    return context!!.createDrawable(
        color,
        radius,
        strokeColor,
        strokeWidth,
        enableRipple,
        rippleColor
    )
}

fun View.createDrawable(
    color: Int = Color.TRANSPARENT, radius: Float = 0f,
    strokeColor: Int = Color.TRANSPARENT, strokeWidth: Int = 0,
    enableRipple: Boolean = true,
    rippleColor: Int = Color.parseColor("#88999999")
): Drawable {
    return context!!.createDrawable(
        color,
        radius,
        strokeColor,
        strokeWidth,
        enableRipple,
        rippleColor
    )
}

fun RecyclerView.ViewHolder.createDrawable(
    color: Int = Color.TRANSPARENT, radius: Float = 0f,
    strokeColor: Int = Color.TRANSPARENT, strokeWidth: Int = 0,
    enableRipple: Boolean = true,
    rippleColor: Int = Color.parseColor("#88999999")
): Drawable {
    return itemView.createDrawable(
        color,
        radius,
        strokeColor,
        strokeWidth,
        enableRipple,
        rippleColor
    )
}

/**
 * 提供的File转换Uri
 */
fun Any.getUriFromPath(file: File): Uri {
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        FileProvider.getUriForFile(commContext(), commContext().packageName + ".file.path.share", file)
    } else {
        Uri.fromFile(file)
    }
}

/** json相关 **/
fun Any.toJson() = Gson().toJson(this)

//内联函数+标注泛型 = 泛型实例化
inline fun <reified T> String.toBean() = Gson().fromJson<T>(this, object : TypeToken<T>() {}.type)


/** Window相关 **/
fun Context.windowWidth(): Int {
    val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
    return windowManager.defaultDisplay.width
}

fun Context.windowHeight(): Int {
    val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
    return windowManager.defaultDisplay.height
}

fun Fragment.windowWidth(): Int {
    return context!!.windowWidth()
}

fun Fragment.windowHeight(): Int {
    return context!!.windowHeight()
}

fun View.windowWidth(): Int {
    return context!!.windowWidth()
}

fun View.windowHeight(): Int {
    return context!!.windowHeight()
}

fun RecyclerView.ViewHolder.windowWidth(): Int {
    return itemView.windowWidth()
}

fun RecyclerView.ViewHolder.windowHeight(): Int {
    return itemView.windowHeight()
}


/** 网络相关 **/
/**
 * 当前网络是否有连接
 */
fun Any.isNetworkConnected() = NetWorkUtil.isConnected(CommUtils.getContext())

/**
 * 当前是否是Wifi连接
 */
fun Any.isWifiConnected() = NetWorkUtil.isWifiConnected(CommUtils.getContext())


/**
 * 检查是否有网络-直接检查全局内存
 * 如果没有网络连接会给出错误吐司气泡
 */
fun Any.checkNetworkConnected(
    block: () -> Unit,
    msg: String = "Network connection error, please check the network connection"
) {

    if (BaseApplication.checkHasNet()) {
        block()
    } else {
        toast(msg)
    }

}

/**
 * 主线程运行
 */
fun Any.runOnUIThread(block: () -> Unit) {
    Handler(Looper.getMainLooper()).post { block() }
}


/**
 * 将Bitmap保存到相册
 */
fun Bitmap.saveToAlbum(
    format: Bitmap.CompressFormat = Bitmap.CompressFormat.PNG,
    quality: Int = 100,
    filename: String = "",
    callback: ((path: String?, uri: Uri?) -> Unit)? = null
) {
    GlobalScope.launch {
        try {
            //1. create path
            val dirPath =
                Environment.getExternalStorageDirectory().absolutePath + "/" + Environment.DIRECTORY_PICTURES
            val dirFile = File(dirPath)
            if (!dirFile.exists()) dirFile.mkdirs()
            val ext = when (format) {
                Bitmap.CompressFormat.PNG -> ".png"
                Bitmap.CompressFormat.JPEG -> ".jpg"
                Bitmap.CompressFormat.WEBP -> ".webp"
                else -> ""
            }
            val target = File(
                dirPath,
                (if (filename.isEmpty()) System.currentTimeMillis().toString() else filename) + ext
            )
            if (target.exists()) target.delete()
            target.createNewFile()
            //2. save
            compress(format, quality, FileOutputStream(target))
            //3. notify
            MediaScannerConnection.scanFile(
                CommUtils.getContext(), arrayOf(target.absolutePath),
                arrayOf("image/$ext")
            ) { path, uri ->
                runOnUIThread {
                    callback?.invoke(path, uri)
                }
            }
        } catch (e: IOException) {
            e.printStackTrace()
            runOnUIThread { callback?.invoke(null, null) }
        }
    }
}

/**
 * 数组转bundle
 */
fun Array<out Pair<String, Any?>>.toBundle(): Bundle? {
    return Bundle().apply {
        forEach { it ->
            val value = it.second
            when (value) {
                null -> putSerializable(it.first, null as Serializable?)
                is Int -> putInt(it.first, value)
                is Long -> putLong(it.first, value)
                is CharSequence -> putCharSequence(it.first, value)
                is String -> putString(it.first, value)
                is Float -> putFloat(it.first, value)
                is Double -> putDouble(it.first, value)
                is Char -> putChar(it.first, value)
                is Short -> putShort(it.first, value)
                is Boolean -> putBoolean(it.first, value)
                is Serializable -> putSerializable(it.first, value)
                is Parcelable -> putParcelable(it.first, value)

                is IntArray -> putIntArray(it.first, value)
                is LongArray -> putLongArray(it.first, value)
                is FloatArray -> putFloatArray(it.first, value)
                is DoubleArray -> putDoubleArray(it.first, value)
                is CharArray -> putCharArray(it.first, value)
                is ShortArray -> putShortArray(it.first, value)
                is BooleanArray -> putBooleanArray(it.first, value)

                is Array<*> -> when {
                    value.isArrayOf<CharSequence>() -> putCharSequenceArray(
                        it.first,
                        value as Array<CharSequence>
                    )

                    value.isArrayOf<String>() -> putStringArray(it.first, value as Array<String>)
                    value.isArrayOf<Parcelable>() -> putParcelableArray(
                        it.first,
                        value as Array<Parcelable>
                    )
                }
            }
        }
    }

}


// =======================  倒计时的实现 ↓ =========================

@ExperimentalCoroutinesApi
fun BaseViewModel.countDown(
    time: Int = 5,
    start: (scope: CoroutineScope) -> Unit,
    end: () -> Unit,
    next: (time: Int) -> Unit
) {

    viewModelScope.launch {
        // 在这个范围内启动的协程会在Lifecycle被销毁的时候自动取消

        flow {
            (time downTo 0).forEach {
                delay(1000)
                emit(it)
            }
        }.onStart {
            // 倒计时开始 ，在这里可以让Button 禁止点击状态
            start(this@launch)

        }.onCompletion {
            // 倒计时结束 ，在这里可以让Button 恢复点击状态
            end()

        }.catch {
            //错误
            MyLogUtils.e(it.message ?: "UnKnow Error")

        }.collect {
            // 在这里 更新值来显示到UI
            next(it)
        }

    }

}

/**
 * 倒计时的实现
 */
@ExperimentalCoroutinesApi
fun FragmentActivity.countDown(
    time: Int = 5,
    start: (scope: CoroutineScope) -> Unit,
    end: () -> Unit,
    next: (time: Int) -> Unit
) {

    lifecycleScope.launch {
        // 在这个范围内启动的协程会在Lifecycle被销毁的时候自动取消

        flow {
            (time downTo 0).forEach {
                delay(1000)
                emit(it)
            }
        }.onStart {
            // 倒计时开始 ，在这里可以让Button 禁止点击状态
            start(this@launch)

        }.onCompletion {
            // 倒计时结束 ，在这里可以让Button 恢复点击状态
            end()

        }.catch {
            //错误
            MyLogUtils.e(it.message ?: "UnKnow Error")

        }.collect {
            // 在这里 更新值来显示到UI
            next(it)
        }

    }
}

@ExperimentalCoroutinesApi
fun Fragment.countDown(
    time: Int = 5,
    start: (scope: CoroutineScope) -> Unit,
    end: () -> Unit,
    next: (time: Int) -> Unit
) {

    lifecycleScope.launch {
        // 在这个范围内启动的协程会在Lifecycle被销毁的时候自动取消

        flow {
            (time downTo 0).forEach {
                delay(1000)
                emit(it)
            }
        }.onStart {
            // 倒计时开始 ，在这里可以让Button 禁止点击状态
            start(this@launch)

        }.onCompletion {
            // 倒计时结束 ，在这里可以让Button 恢复点击状态
            end()

        }.catch {
            //错误
            MyLogUtils.e(it.message ?: "UnKnow Error")

        }.collect {
            // 在这里 更新值来显示到UI
            next(it)
        }

    }
}

//带错误，取消，更好用的Flow倒计时
/**
private var job: Job? = null

job = lifecycleScope.countDownFlow(
onStart = {
YYLogUtils.w("开始倒计时")
},
onEach = {
if (it == 50) {
job?.cancel()
}
YYLogUtils.w("当前倒计时:$it")
},
onCancel = {
YYLogUtils.w("倒计时出错了:$it")
},
onCompletion = {
YYLogUtils.w("完成倒计时")
}
)
 */
fun CoroutineScope.countDownByFlow(
    time: Int = 60,
    onStart: (suspend () -> Unit)? = null,
    onEach: (suspend (Int) -> Unit)? = null,
    onCancel: (suspend (msg: String?) -> Unit)? = null,
    onCompletion: (suspend () -> Unit)? = null
): Job {
    return (time downTo 0)
        .asFlow()
        .cancellable()
        .flowOn(Dispatchers.Default)
        .onStart {
            onStart?.invoke()
        }
        .onEach {
            onEach?.invoke(it)
            delay(1000L)
        }
        .onCompletion {
            if (it == null) {
                onCompletion?.invoke() // 正常完成
            } else {
                onCancel?.invoke(it.message) // 取消时执行取消回调
            }
        }
        .launchIn(this)

}

/**
 * 登录的校验
 */
fun Any.extLoginCheck(loginBlock: () -> Unit, taskBlock: () -> Unit) {

    object : LoginInterceptorTask() {

        override fun isLogin(): Boolean {
            return BaseApplication.isUserLogin
        }

        override fun gotoLogin() {
            loginBlock()
        }

    }.execute {
        taskBlock()
    }
}

/**
 * 刷新登录的状态-通知给任务器
 */
fun Any.extLoginEnd() {
    //不管登录成功还是失败，完成登录通知一下登录拦截器放行
    LoginInterceptorTask.loginEnded()
}
